﻿
//    MIT License
//    
//    Copyright (c) 2017 Dustin Whirle
//    
//    My Youtube stuff: https://www.youtube.com/playlist?list=PL-sp8pM7xzbVls1NovXqwgfBQiwhTA_Ya
//    
//    Permission is hereby granted, free of charge, to any person obtaining a copy
//    of this software and associated documentation files (the "Software"), to deal
//    in the Software without restriction, including without limitation the rights
//    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//    copies of the Software, and to permit persons to whom the Software is
//    furnished to do so, subject to the following conditions:
//    
//    The above copyright notice and this permission notice shall be included in all
//    copies or substantial portions of the Software.
//    
//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//    SOFTWARE.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;


namespace BLINDED_AM_ME{
	
	public class Mesh_Maker{

		// Mesh Values
		private List<Vector3>   _vertices  = new List<Vector3>();
		private List<Vector3>   _normals   = new List<Vector3>();
		private List<Vector2>   _uvs       = new List<Vector2>();
		private List<Vector4>   _tangents  = new List<Vector4>();
		private List<List<int>> _subIndices = new List<List<int>>();
        
	    public int VertCount
        {
            get
            {
                return _vertices.Count;
            }
        }

        /// <summary>
        /// Clears all arrays
        /// </summary>
        public void Clear()
        {
            _vertices.Clear();
            _normals.Clear();
            _uvs.Clear();
            _tangents.Clear();
            _subIndices.Clear();
        }

        public void AddTriangle(Triangle triangle, int submesh)
        {
            AddTriangle(triangle.vertices, triangle.uvs, triangle.normals, triangle.tangents, submesh);
        }

        /// <summary>
        /// Adds a new triangle to the return of GetMesh()
        /// </summary>
        /// <param name="vertices">Array of 3</param>
        /// <param name="normals">Array of 3</param>
        /// <param name="uvs">Array of 3</param>
        /// <param name="submesh">If you don't know put 0</param>
		public void AddTriangle(Vector3[] vertices, Vector2[] uvs, Vector3[] normals, int submesh = 0)
        {
            AddTriangle(vertices, uvs, normals, null, submesh);
		}
        /// <summary>
        /// Same as the first, but with tangents
        /// </summary>
        /// <param name="vertices">Array of 3</param>
        /// <param name="normals">Array of 3</param>
        /// <param name="uvs">Array of 3</param>
        /// <param name="tangents">Array of 3</param>
        /// <param name="submesh">If you don't know put 0</param>
        public void AddTriangle(Vector3[] vertices, Vector2[] uvs, Vector3[] normals, Vector4[] tangents, int submesh = 0)
        {
			int vertCount = _vertices.Count;

			_vertices.Add(vertices[0]);
			_vertices.Add(vertices[1]);
			_vertices.Add(vertices[2]);

			_normals.Add(normals[0]);
			_normals.Add(normals[1]);
			_normals.Add(normals[2]);

			_uvs.Add(uvs[0]);
			_uvs.Add(uvs[1]);
			_uvs.Add(uvs[2]);

            if (tangents != null)
            {
                _tangents.Add(tangents[0]);
                _tangents.Add(tangents[1]);
                _tangents.Add(tangents[2]);
            }

			if(_subIndices.Count < submesh+1){
				for(int i=_subIndices.Count; i<submesh+1; i++){
					_subIndices.Add(new List<int>());
				}
			}

			_subIndices[submesh].Add(vertCount);
			_subIndices[submesh].Add(vertCount+1);
			_subIndices[submesh].Add(vertCount+2);

		}

        /// <summary>
        /// Cleans up Double Vertices
        /// </summary>
		public void RemoveDoubles(){

			int dubCount = 0;

			Vector3 vertex = Vector3.zero;
			Vector3 normal = Vector3.zero;
			Vector2 uv     = Vector2.zero;
			Vector4 tangent= Vector4.zero;

			int iterator=0; 
			while(iterator < VertCount){

				vertex = _vertices[iterator];
				normal = _normals[iterator];
				uv     = _uvs[iterator];

				// look backwards for a match
				for(int backward_iterator=iterator-1; backward_iterator>=0; backward_iterator--){

					if(vertex  == _vertices[backward_iterator] &&
						normal == _normals[backward_iterator] && 
						uv     == _uvs[backward_iterator])
					{
						dubCount++;
						DoubleFound(backward_iterator, iterator);
						iterator--;
						break; // there should only be one
					}
				}

				iterator++;

			} // while

			Debug.LogFormat("Doubles found {0}", dubCount);
				
		}
        /// <summary>
        /// // go through all indices an replace them
        /// </summary>
        /// <param name="first"></param>
        /// <param name="duplicate"></param>
		private void DoubleFound(int first, int duplicate)
        {
			for(int h=0; h<_subIndices.Count; h++){
				for(int i=0; i<_subIndices[h].Count; i++){

					if(_subIndices[h][i] > duplicate) // knock it down
						_subIndices[h][i]--;
					else if( _subIndices[h][i] == duplicate) // replace
						_subIndices[h][i] = first;
				}
			}

			_vertices.RemoveAt(duplicate);
			_normals.RemoveAt(duplicate);
			_uvs.RemoveAt(duplicate);

			if(_tangents.Count > 0)
				_tangents.RemoveAt(duplicate);

		}

		/// <summary>
		/// Creates and returns a new mesh
		/// </summary>
		public Mesh GetMesh(){
			
			Mesh shape = new Mesh();
			shape.name =  "Generated Mesh";
			shape.SetVertices(_vertices);
			shape.SetNormals(_normals);
			shape.SetUVs(0, _uvs);
			shape.SetUVs(1, _uvs);
            
			if(_tangents.Count > 1)
				shape.SetTangents(_tangents);

			shape.subMeshCount = _subIndices.Count;

			for(int i=0; i<_subIndices.Count; i++)
				shape.SetTriangles(_subIndices[i], i);

			return shape;
		}

		#if UNITY_EDITOR
		/// <summary>
		/// Creates and returns a new mesh with generated lightmap uvs (Editor Only)
		/// </summary>
		public Mesh GetMesh_GenerateSecondaryUVSet(){

			Mesh shape = GetMesh();
		
			// for light mapping
			UnityEditor.Unwrapping.GenerateSecondaryUVSet(shape);

			return shape;
		}

		/// <summary>
		/// Creates and returns a new mesh with generated lightmap uvs (Editor Only)
		/// </summary>
		public Mesh GetMesh_GenerateSecondaryUVSet( UnityEditor.UnwrapParam param){

			Mesh shape = GetMesh();

			// for light mapping
			UnityEditor.Unwrapping.GenerateSecondaryUVSet(shape, param);

			return shape;
		}
		#endif
        
        /// <summary>
        /// every property should have 3 elements
        /// </summary>
        public struct Triangle
        {
            public Vector3[] vertices;
            public Vector2[] uvs;
            public Vector3[] normals;
            public Vector4[] tangents;

            public Triangle(Vector3[] vertices = null, Vector2[] uvs = null, Vector3[] normals = null, Vector4[] tangents = null)
            {               
                this.vertices = vertices;
                this.uvs      = uvs;      
                this.normals  = normals;
                this.tangents = tangents;
            }
        }
        
	}
}